﻿package evo.engine
{
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.utils.*;

	public class Sequencer extends Sound {
		private var bpm:Number = 125;
		private var tickTime:Number; 
		private var intervalId:Number;
		private var isPlay:Boolean = false;
		private var offset:Number = 0;
		private var tickListeners:Array = new Array();
		private var tickListenerList:Array = new Array();
		private var tickListenerId:uint = 0;
		private var sndChannel:SoundChannel;
		
		private var tickers:Array=new Array();
		private var tickAddPos:Number=0;
		private var tickPos:Number=0;
		
		private var startTime:Number=0;
		private var truePlay:Boolean = true;
		
		/**
		 * 
		 * This is a mandatory synch event type. Demo engine will use it to switch between parts automatically.
		 **/
		public var phase:Tick=new Tick();
	
		public function set isTruePlay(truePlay:Boolean):void
		{
			this.truePlay = truePlay;
		}
		
		public function setBPM(bpm:Number):void
		{
			this.bpm = bpm;
			this.tickTime = 60000 / (bpm * 4);
			missTime = tickTime;
		}
		
		public function setOffset(offset:Number):void
		{
			this.offset = offset;
		}
		
		public override function play(startTime:Number = 0, loops:int = 0, sndTransform:SoundTransform = null):SoundChannel 
		{
		//	trace("TruePlay:"+truePlay);
			isPlay = true;
			if(truePlay)
			{
				sndChannel=super.play(startTime, loops, sndTransform);		
			}
			else
			{
				startTime = getTimer();
			}
			//sndChannel=super.play(0, loops, sndTransform);
			//intervalId=setInterval(this.tick, 1/30);

			return sndChannel;
		}
		
		public function addTickers(...args) :void
		{
			for each(var ticker:TickPattern in args) {
				for(var t:int=0; t < ticker.ticks.length; t++) {
					var obj:Array=tickers[ticker.ticks[t]+tickAddPos];
					if(obj==null) obj=new Array();
					obj.push(ticker.ticker);
					
					tickers[ticker.ticks[t]+tickAddPos]=obj;
				}
			}
			
			tickAddPos += 4*4;
		}
		
		public function stop():void
		{
			isPlay = false;
			this.stop();
			clearInterval(intervalId);
		}
		
		public function fireTick():void
		{
			if(tickers[tickPos] != undefined) {
				for each(var ticker:Tick in tickers[tickPos])
					ticker.run(tickPos);
			}
			tickPos+=1;
		}
		
		private var tickOffset:Number = 0;
		private var lastTickTime:Number = 0;
		private var lastRealTickTime:Number = 0;
		private var missTime:Number;
		
		public function tick():void
		{
			if(!isPlay)	{
				return;
			}
			var acTickTime:Number;
			
			if(truePlay)
			{
				acTickTime = (sndChannel.position-offset)-lastTickTime;  // tickTime + error
				lastTickTime = (sndChannel.position-offset);
			}
			else
			{
				var position:Number = getTimer()-startTime;
				acTickTime = (position-offset)-lastTickTime;  // tickTime + error
				lastTickTime = (position-offset);
			}
			
			missTime -= (acTickTime);  
			
			while(missTime < -tickOffset) {
				tickOffset = missTime+tickOffset;
				missTime = tickTime;
				fireTick();
			}
		}
	}
}